实验七 设备驱动程序实践 by lilan.docx |
您所在的位置:网站首页 › insmod init › 实验七 设备驱动程序实践 by lilan.docx |
实验七 设备驱动程序实践 by lilan.docx 《实验七 设备驱动程序实践 by lilan.docx》由会员分享,可在线阅读,更多相关《实验七 设备驱动程序实践 by lilan.docx(16页珍藏版)》请在冰豆网上搜索。 实验七设备驱动程序实践bylilan 实验七设备驱动程序实践 【实验目的】 配合操作系统课程的学习,加深对设备驱动程序的理解。 【实验学时】 建议2学时 【实验内容】 进行简单的字符驱动模块的编写,动态加载所生成的驱动,编写并调用应用层测试程序,测试结束后对驱动模块进行卸载,注意观察驱动加载和卸载时所提示的消息。 【实验原理】 Linux下的设备驱动程序是一些用于完成不同任务的函数的集合,通过这些函数使得linux下的设备犹如普通文件一般。 因此对于应用程序来说设备只是一个普通的文件,应用程序可以象操作普通文件一样对硬件设备进行打开、关闭、读、写、IO控制等操作。 Linux下的驱动主要分为字符设备驱动、块设备驱动和流设备驱动三类。 字符设备是指设备发送和接收数据以字符的形式进行;块设备以整个数据缓冲区为发送和接收的对象;流设备主要应用于网络通信方面。 Linux设备驱动属于内核的一部分,我们可以通过两种方式对其进行编译和加载: 1).与内核一同编译,使其随linux的启动而自动加载。 2).编译成一个可加载和删除的模块,在linux运行过程中使用命令对其进行动态的加载和卸载。 该方式控制了内核的大小,当模块被加载入内核后它就和内核中其它驱动模块没有区别。 Linux驱动编写的最小框架如下所示: #include头文件 MODULE_LICENSE(“GPL”); staticint__initname_init(void) { 模块加载时执行的相关操作 return0; } staticvoid__exitname_exit(void) { 模块卸载时执行的相关操作 } module_init(name_init); module_exit(name_exit); 从上面可以看出,一个linux最小驱动模块所必需的组成部分为模块初始化函数和模块卸载函数,前者在模块加载时运行,后者在模块卸载时运行。 其中MODULE_LICENSE(“GPL”)用来声明一个模块的许可证。 在驱动模块的编程过程中我们还需注意的是其输出语句与内存分配语言与应用层中的写法不同,这是由其内核态编程模式决定的,在内核态编程中其写法分别为printk()、kmalloc()。 字符设备驱动模块的动态加载和卸载可以采用命令insmod和rmmod,如果因系统版本问题导致无法动态加载则可以加入命令参数-f进行强制加载。 加载和卸载驱动的过程中我们可以用dmesg|tail命令来察看模块是否已成功加载或卸载,这里察看的内容实际上就是我们上面提到的最小驱动编写中的name_init()和name_exit()两个函数中显示的内容。 应用层的编写主要针对驱动中的读写函数来实现简单的输入读出操作,本例中我们将简单地以输入字符到驱动中,并从驱动中读出字符来验证驱动程序的正确性。 【实验要求】 1.正确生成helloworld驱动程序,并挂载和卸载它。 要求会写简单的设备驱动程序。 2.正确生成字符设备驱动程序,并挂载和卸载它,同时通过3个函数读、写以及查看设备状态。 【实验步骤】 1、安装ubuntu10.10 2、安装gcc: Sudoapt-getinstallbuild-essential Sudoapt-getinstallgdb 3、使用如下步骤生成附件所给的helloworld驱动程序。 (1)先检查一下系统的内核版本和内核头文件版本是一致,实验用Ubuntu10.10,内核是2.6.35-22-generic。 分别在终端中输入命令uname-r和ls/usr/src/,得到内核版本。 如果内核版本和内核头文件版不一致,则在insmod一步会出现错误 (2)编写hellodriver.c文件 在主文件夹目录下建一个hellodriver目录(文件夹),新建hellodriver.c文件,输入以下内容: #include/src/linux-headers-2.6.35-22/include/linux/init.h> #include/src/linux-headers-2.6.35-22/include/linux/module.h> MODULE_LICENSE("DualBSD/GPL"); staticinthello_init(void) { printk(KERN_ALERT"Hello,World! \n"); return0; } staticvoidhello_exit(void) { printk(KERN_ALERT"Goodbye,cruelworld\n"); } module_init(hello_init); module_exit(hello_exit); (3)编写Makefile 在/home/li/share/CodeTest/hellodriver/下新建Makefile文件,输入下面内容: KERNELDIR=/lib/modules/2.6.35-22-generic/build PWD: =$(shellpwd) INSTALLDIR=/home/li/share/CodeTest/hellodriver/install obj-m: =hellodriver.o modules: $(MAKE)-C$(KERNELDIR)M=$(PWD)modules modules_install: cphellodriver.ko$(INSTALLDIR) clean: rm-rf*.o*~core.depend.*.cmd*.ko*.mod.c.tmp_versions .PHONY: modulesmodules_installclean 注意绿色部分代码根据自己实际情况进行修改,还有cphellodriver.ko$(INSTALLDIR)和rm-rf*.o*~core.depend.*.cmd*.ko*.mod.c.tmp_versions两条语句前有,否则在make时会出现错误。 (4)进行make 在终端输入命令: sudomake-C/lib/modules/2.6.35-22-generic/buildM=/home/li/share/CodeTest/hellodrivermodules 这里有三点要注意: 1、命令中的2.6.35-22-generic是内核版本,要根据目前使用的系统的内核版本进行修改; 2、M=后面的是hellodriver目录,也要根据实际情况进行修改; 3、make需要较高的权限,所以前面要加sudo,否则会出现错误 3、安装模块 安装用insmod加目录下的hellodriver.ko命令,安装完后可以用lsmod查看是否安装成功。 insmod/home/li/share/CodeTest/hellodriver/hellodriver.ko 可以看到在第一个位置就是我们的Hello模块。 在安装时执行了hellodriver.c中 staticinthello_init(void) { printk(KERN_ALERT"Hello,World! \n"); return0; }这段代码,要查看printk()中"Hello,World! "的信息可以在终端键入dmesg|tail-5(显示后五条)命令。 最后一条的"Hello,World! "就是我们刚执行的安装Hello时模块操作写入到系统日志中去的。 4、卸载模块 用rmmodHello命名,用lsmod查看是否卸载成功。 可以看到Hello模块没了。 同样用dmesg|tail-5命令查看一下卸载时hellodriver.c中 staticvoidhello_exit(void) { printk(KERN_ALERT"Goodbye,cruelworld\n"); }代码的执行情况。 4、使用同样步骤生成附件所给的字符设备驱动程序。 并用三个c文件使用设备驱动。 驱动程序完成的主要工作是初始化,添加,删除结构体cdev 申请和释放设备号 填充file_operation结构体中的操作函数。 参考如下步骤: (1)生成驱动程序 sudomake-C/lib/modules/2.6.35-22-generic/buildM=/home/li/share/CodeTest/chardrivermodules (2)安装驱动程序 insmod/home/li/share/CodeTest/chardriver/Character_Device.ko 可使用: Echolilan>/dev/Character_Device验证设备的写 Cat/dev/Character_Device验证设备的读功能 查看设备号 cat/proc/devices (3)建立文件结点 mknod/dev/Character_Devicec2500 查看设备文件结点: ls/dev (4)在应用层使用驱动程序 ./testWriter ./testReader ./testCtrl 卸载设备 rmmodCharacter_Device rm-r/dev/Character_Device 源码如下: //Character_Device.c #include/src/linux-headers-2.6.35-22/include/linux/init.h> #include/src/linux-headers-2.6.35-22/include/linux/module.h> #include/src/linux-headers-2.6.35-22/include/linux/kernel.h> #include/src/linux-headers-2.6.35-22/include/linux/fs.h> #include/src/linux-headers-2.6.35-22/include/linux/cdev.h> #include/src/linux-headers-2.6.35-22/include/linux/ioctl.h> #include/src/linux-headers-2.6.35-22/include/linux/capability.h> #include #defineIOC_MAGIC'k' #defineSET_STATE_IO(IOC_MAGIC,1) #defineGET_STATE_IOR(IOC_MAGIC,2,int) #defineCLEAR_BUF_IO(IOC_MAGIC,3) #defineIOC_MAXNR14 intstate;
#defineDEVICE_NAME"Character_Device" #defineMAX_BUF_SIZE300 staticunsignedcharCharacter_Device_buf[MAX_BUF_SIZE]; staticintCharacter_Device_open(structinode*inode,structfile*file); staticintCharacter_Device_release(structinode*inode,structfile*file); staticssize_tCharacter_Device_read(structfile*file,char__user*buf,size_tcount,loff_t*pos); staticssize_tCharacter_Device_write(structfile*file,constchar__user*buf,size_tcount,loff_t*pos); staticintCharacter_Device_open(structinode*inode,structfile*file) { return0; } staticintCharacter_Device_release(structinode*inode,structfile*file) { return0; } staticssize_tCharacter_Device_read(structfile*file,char__user*buf,size_tcount,loff_t*pos) { intsize=count count: MAX_BUF_SIZE; if(copy_to_user(buf,Character_Device_buf,size)) return-ENOMEM; printk(KERN_ALERT"用户从Character_Device设备中读取了数据: "); printk(Character_Device_buf); returnsize; } staticssize_tCharacter_Device_write(structfile*filp,constchar__user*buf,size_tcount,loff_t*pos) { intsize=count count: MAX_BUF_SIZE; memset(Character_Device_buf,0,sizeof(Character_Device_buf)); if(copy_from_user(Character_Device_buf,buf,size)) return-ENOMEM; printk(KERN_ALERT"用户往Character_Device设备中写入了数据: "); printk(Character_Device_buf); returnsize; } intCharacter_Device_ioctl(structinode*inode,structfile*filp,unsignedintcmd,unsignedlongarg) { interr=0; intretval=0; if(_IOC_TYPE(cmd)! =IOC_MAGIC) return-ENOTTY; if(_IOC_NR(cmd)>IOC_MAXNR) return-ENOTTY; if(_IOC_DIR(cmd)&_IOC_READ) err=! access_ok(VERIFY_WRITE,(void__user*)arg,_IOC_SIZE(cmd)); elseif(_IOC_DIR(cmd)&_IOC_WRITE) err=! access_ok(VERIFY_READ,(void__user*)arg,_IOC_SIZE(cmd)); if(err) return-EFAULT; switch(cmd) { caseSET_STATE: if(! capable(CAP_SYS_ADMIN)) return-EPERM; state=arg; break; caseGET_STATE: retval=__put_user(state,(int__user*)arg); break; caseCLEAR_BUF: memset(Character_Device_buf,0,sizeof(Character_Device_buf)); break; default: return-ENOTTY; } returnretval; }
staticstructfile_operationsCharacter_Device_fops={ .read=Character_Device_read, .write=Character_Device_write, .ioctl=Character_Device_ioctl, .open=Character_Device_open, .release=Character_Device_release, }; staticstructcdev*Character_Device_cdev;//设备结构体实例 staticint__initCharacter_Device_init(void)//字符设备驱动模块加载函数 { dev_tdev; intflag; flag=alloc_chrdev_region(&dev,0,2,DEVICE_NAME);//动态获得主设备号 if(flag) { printk(KERN_ALERT"动态分配设备号失败! \n"); returnflag; } Character_Device_cdev=cdev_alloc();//动态申请一cdev内存 if(Character_Device_cdev==NULL) { printk(KERN_ALERT"动态分配字符设备对象失败! \n"); unregister_chrdev_region(dev,2); return-ENOMEM; } Character_Device_cdev->ops=&Character_Device_fops; Character_Device_cdev->owner=THIS_MODULE; flag=cdev_add(Character_Device_cdev,dev,1); if(flag) { printk(KERN_ALERT"设备Character_Device添加失败! \n"); unregister_chrdev_region(dev,2); cdev_del(Character_Device_cdev); returnflag; } else printk(KERN_ALERT"设备Character_Device添加成功! \n"); memset(Character_Device_buf,0,sizeof(Character_Device_buf)); state=0; return0; } staticvoid__exitCharacter_Device_exit(void) { unregister_chrdev_region(Character_Device_cdev->dev,2); cdev_del(Character_Device_cdev); printk(KERN_ALERT"设备Character_Device删除成功! \n"); } module_init(Character_Device_init); module_exit(Character_Device_exit); MODULE_LICENSE("GPL");
//Makefile DEBFLAGS=-O2 EXTRA_CFLAGS+=$(DEBFLAGS) #如果已定义KERNELRELEASE,则说明是从内核构造系统调用的,因此可利用其内建语句。 ifneq($(KERNELRELEASE),) xbrige-objs: =Character_Device.o obj-m: =Character_Device.o #否则,是直接从命令行调用的,这时要调用内核构造系统 else KERNELDIR? =/lib/modules/$(shelluname-r)/build PWD: =$(shellpwd) modules: $(MAKE)-C$(KERNELDIR)M=$(PWD)modules endif clean: rm-rf*.o*~core.depend.*.cmd*.ko*.mod.c.tmp_versions depend.dependdep: $(CC)$(CFLAGS)$(EXTRA_CFLAGS)-M*.c>.depend ifeq(.depend,$(wildcard.depend)) include.depend endif
//testReader.c #include #include #include #include #include #include #defineIOC_MAGIC'k' #defineSET_STATE_IO(IOC_MAGIC,1) #defineGET_STATE_IOR(IOC_MAGIC,2,int) #defineMAX_BUF_SIZE300 intmain() { intCharacter_Device; inti; intstate; charbuf[MAX_BUF_SIZE]; memset(buf,0,sizeof(buf)); if((Character_Device=open("/dev/Character_Device",O_RDWR))==-1) { printf("文件打开失败,设备不存在! \n"); return-1; } printf("\n您已进入Character_Device设备的读取控制测试程序\n"); ioctl(Character_Device,GET_STATE,&state); printf("设备Character_Device当前的状态为: "); switch(state) { case0: printf("可用\n"); read(Character_Device,buf,MAX_BUF_SIZE); printf("Character_Device缓冲区中的数据为: \n"); for(i=0;i printf("%c",buf[i]); printf("\n"); ioctl(Character_Device,SET_STATE,3); printf("请确认读取操作结束,放弃对设备Character_Device的占用(回车键): "); getchar(); ioctl(Character_Device,SET_STATE,0); break; case1: printf("不可用\n"); |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |